﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace TestChemicalEquilibrium
{
    public partial class FormMain : Form
    {
        Random R = new Random();

        List<ThreadEventWait> TEWs = new List<ThreadEventWait>();

        Queue<Reactant> PcT_ = new Queue<Reactant>();
        Queue<Reactant> PrT = new Queue<Reactant>();
        Queue<Reactant> Ts = new Queue<Reactant>();
        Queue<Reactant> T0s = new Queue<Reactant>();
        Queue<Reactant> T_s = new Queue<Reactant>();
        Queue<Reactant> Rs = new Queue<Reactant>();
        Queue<Reactant> TRs = new Queue<Reactant>();
        Queue<Reactant> TR_s = new Queue<Reactant>();

        Queue<Reactant> G1s = new Queue<Reactant>();
        Queue<Reactant> G2s = new Queue<Reactant>();
        Queue<Reactant> G1G2s = new Queue<Reactant>();
        Queue<Reactant> G1G2_s = new Queue<Reactant>();

        Queue<Reactant> KChannel = new Queue<Reactant>();
        Queue<Reactant> K = new Queue<Reactant>();
        Queue<Reactant> K_ = new Queue<Reactant>();
        Queue<Reactant> NaChannel = new Queue<Reactant>();
        Queue<Reactant> Na = new Queue<Reactant>();
        Queue<Reactant> Na_ = new Queue<Reactant>();
        Queue<Reactant> NaKPump = new Queue<Reactant>();

        FormDiagram FDReactant, FDIon;

        ReactActivate RA_CreateT, RA_InactivateT;
        ReactCombineResolve RCR_TandRtoTR, RCR_G1G2toG1andG2withTR;

        IonicEquilibrium IE_K, IE_Na, IE_Na_T;
        IonicPump IP_NaK;

        public FormMain()
        {
            InitializeComponent();

            this.BuildReactant(1000, this.PcT_, this.PrT, this.T_s, this.Rs, this.TR_s);
            this.BuildReactantCombined(10000, this.G1G2s);

            this.BuildReactant(100000,this.K, this.K_, this.Na, this.Na_);

            this.BuildReactant(1000, this.KChannel, this.NaChannel);
            this.BuildReactant(10, this.NaKPump);

            //[1]
            this.RA_CreateT = new ReactActivate(this.T0s, this.T_s, null, this.PrT, 10, 10, 1, 1);
            //[2]
            this.RA_InactivateT = new ReactActivate(this.Ts, this.T_s, this.PcT_, null, 10, 10, 1, 1);
            //[3][4]
            this.RCR_TandRtoTR = new ReactCombineResolve(this.Ts, this.Rs, this.TRs, this.TR_s, null, 5, 10, 1, 1);
            //[5]
            this.RCR_G1G2toG1andG2withTR = new ReactCombineResolve(this.G1s, this.G2s, this.G1G2s, this.G1G2_s, this.TRs, 10, 10, 1, 10);

            this.IE_K = new IonicEquilibrium(this.KChannel, this.K, this.K_, 0.009f, -80, this.GetVoltage);
            this.IE_Na = new IonicEquilibrium(this.NaChannel, this.Na, this.Na_, 0.001f, 62, this.GetVoltage);
            this.IP_NaK = new IonicPump(this.NaKPump, new Queue<Reactant>[] { this.K, this.Na }, new Queue<Reactant>[] { this.K_, this.Na_ }, new int[] { 200, -200 }, new int[] { 90000, 110000 });

            this.IE_Na_T = new IonicEquilibrium(this.TRs, this.Na, this.Na_, 0.01f, 62, this.GetVoltage);

            this.TEWs.Add(new ThreadEventWait(100, new OperateCallback(this.RA_CreateT.Activate)));
            this.TEWs.Add(new ThreadEventWait(100, new OperateCallback(this.RA_InactivateT.Inactivate)));
            this.TEWs.Add(new ThreadEventWait(100, new OperateCallback(this.RCR_TandRtoTR.Combine)));
            this.TEWs.Add(new ThreadEventWait(100, new OperateCallback(this.RCR_TandRtoTR.Resolve)));
            this.TEWs.Add(new ThreadEventWait(100, new OperateCallback(this.RCR_G1G2toG1andG2withTR.Resolve)));

            this.TEWs.Add(new ThreadEventWait(100, new OperateCallback(this.IE_K.Transport)));
            this.TEWs.Add(new ThreadEventWait(100, new OperateCallback(this.IE_Na.Transport)));
            this.TEWs.Add(new ThreadEventWait(100, new OperateCallback(this.IP_NaK.Transport)));
            this.TEWs.Add(new ThreadEventWait(100, new OperateCallback(this.IE_Na_T.Transport)));

            this.FDReactant = new FormDiagram(
                new FormDiagram.Data("T0", Color.Lime, new Rectangle(0, 0, 300, 100), this.RA_CreateT.GetACount),
                new FormDiagram.Data("T_", Color.Lime, new Rectangle(0, 110, 300, 100), this.RA_CreateT.GetA_Count),
                new FormDiagram.Data("T", Color.Lime, new Rectangle(0, 220, 300, 100), this.RA_InactivateT.GetACount),
                new FormDiagram.Data("R", Color.Red, new Rectangle(0, 330, 300, 100), this.RCR_TandRtoTR.GetBCount),
                new FormDiagram.Data("TR", Color.Yellow, new Rectangle(0, 440, 300, 100), this.RCR_TandRtoTR.GetABCount),
                new FormDiagram.Data("G1", Color.Lime, new Rectangle(500, 0, 300, 100), this.RCR_G1G2toG1andG2withTR.GetACount),
                new FormDiagram.Data("G2", Color.Lime, new Rectangle(500, 110, 300, 100), this.RCR_G1G2toG1andG2withTR.GetBCount),
                new FormDiagram.Data("G1G2", Color.Lime, new Rectangle(500, 220, 300, 100), this.RCR_G1G2toG1andG2withTR.GetABCount)
                                    );

            this.FDIon = new FormDiagram(
                new FormDiagram.Data("Voltage", Color.Lime, new Rectangle(0, 0, 300, 100), this.GetVoltage),
                new FormDiagram.Data("K Channel", Color.Lime, new Rectangle(0, 110, 300, 100), this.IE_K.GetChannel),
                new FormDiagram.Data("K In", Color.Lime, new Rectangle(0, 220, 300, 100), this.IE_K.GetIonIn),
                new FormDiagram.Data("K Out", Color.Lime, new Rectangle(0, 330, 300, 100), this.IE_K.GetIonOut),
                new FormDiagram.Data("K Count", Color.Lime, new Rectangle(0, 440, 300, 100), this.IE_K.GetCount),
                new FormDiagram.Data("Na Channel", Color.Lime, new Rectangle(500, 110, 300, 100), this.IE_Na.GetChannel),
                new FormDiagram.Data("Na In", Color.Lime, new Rectangle(500, 220, 300, 100), this.IE_Na.GetIonIn), 
                new FormDiagram.Data("Na Out", Color.Lime, new Rectangle(500, 330, 300, 100), this.IE_Na.GetIonOut),
                new FormDiagram.Data("Na Count", Color.Lime, new Rectangle(500, 440, 300, 100), this.IE_Na.GetCount)
                );
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            this.Start();
        }

        protected override void OnClosing(CancelEventArgs e)
        {
            base.OnClosing(e);

            this.Abort();
        }

        public void Start()
        {
            foreach (ThreadEventWait TEW in TEWs) TEW.Start();
        }

        public void Abort()
        {
            foreach (ThreadEventWait TEW in TEWs) TEW.Abort();
        }

        void BuildReactant(int count, params Queue<Reactant>[] reactants)
        {
            foreach (Queue<Reactant> item in reactants)
            {
                for (int i = 0; i < count; i++)
                {
                    item.Enqueue(new Reactant());
                }
            }
        }

        void BuildReactantCombined(int count, params Queue<Reactant>[] reactants)
        {
            foreach (Queue<Reactant> item in reactants)
            {
                for (int i = 0; i < count; i++)
                {
                    item.Enqueue(new Reactant(new Reactant(), new Reactant()));
                }
            }
        }

        float Capacitance = 0.001f;
        int GetVoltage()
        {
            int voltage = (int)((this.K.Count + this.Na.Count - this.K_.Count - this.Na_.Count) * this.Capacitance);
            return voltage;
        }

        void OperateT0toT()
        {
            //int count = R.Next(1000);

            for (int i = 0; i < 1000; i++)
            {
                if (this.T0s.Count == 0) break;

                Ts.Enqueue(this.T0s.Dequeue());
            }
        }

        private void bShowDiagram_Click(object sender, EventArgs e)
        {
            if (!FDReactant.IsDisposed) FDReactant.Show();
        }

        private void bFunction1_Click(object sender, EventArgs e)
        {
            this.OperateT0toT();
        }

        private void bFunction2_Click(object sender, EventArgs e)
        {
        }

        private void bShowIon_Click(object sender, EventArgs e)
        {
            if (!this.FDIon.IsDisposed) this.FDIon.Show();
        }
    }
}
